// Package ioc
/**
* @Project : geektime-basic-go-study
* @File    : middleware.go
* @IDE     : GoLand
* @Author  : Tvux
* @Date    : 2024/10/7 22:31
**/

package ioc

import (
	"fmt"
	ijwt "geektime-basic-go-study/webook/backend/internal/web/jwt"
	"geektime-basic-go-study/webook/backend/internal/web/middleware"
	"github.com/HJH0924/GenericGo/ratelimiter"
	"strings"
	"time"

	"github.com/HJH0924/GenericGo/ginx/accesslog"
	"github.com/HJH0924/GenericGo/ginx/ratelimit"
	"github.com/HJH0924/GenericGo/logger"
	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
	"golang.org/x/net/context"

	"github.com/gin-contrib/cors"
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/memstore"
	redis2 "github.com/gin-contrib/sessions/redis"
	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
)

// GetCORSMiddleware 返回跨域中间件
func GetCORSMiddleware() gin.HandlerFunc {
	// 跨域
	// refer: https://github.com/gin-contrib/cors
	return cors.New(cors.Config{
		AllowHeaders: []string{"Content-Type", "Authorization"},
		// 是否允许你带 cookie 之类的东西
		AllowCredentials: true,
		ExposeHeaders:    []string{"x-access-token", "x-refresh-token"},
		AllowOriginFunc: func(origin string) bool {
			if strings.HasPrefix(origin, "http://") {
				// 开发环境
				return true
			} else if strings.HasPrefix(origin, "webook.com") {
				// k8s
				return true
			}
			// 生产环境
			return strings.Contains(origin, "your_company.com")
		},
		MaxAge: 12 * time.Hour,
	})
}

// GetSessionMiddleware 返回会话中间件
func GetSessionMiddleware() gin.HandlerFunc {
	// session
	// NewStore 这里 单实例部署 可以选择 memstore 的实现
	// 具体参数的含义可以点进去看注释，前面一个是 Authentication Key 后一个是 Encryption Key
	store := memstore.NewStore(
		[]byte("0032c42925316af8f2199dcfaccae67a16189bf29f366697df1fa84f15ae19231b0e7babdbf613a8be0ee0a9820d76ef439083e47c95203089d1912c10eeb92a"),
		[]byte("d41bdea1a7074245d93baa8f5741d0911cd01faa7ea8549fbbe50408c573ddc2"),
	)
	// 但是 多实例部署的话 只能选择 Redis 的实现
	store, err := redis2.NewStore(10, "tcp", "localhost:6379", "", []byte("secret"))
	if err != nil {
		panic(err)
	}
	return sessions.Sessions("ssid", store)
}

// GetRateLimitMiddleware 返回限流中间件
func GetRateLimitMiddleware(redisClient redis.Cmdable, zl logger.Logger) gin.HandlerFunc {
	// 限流
	// 如果要进行压力测试，就不要引入限流的中间件了
	// 注释下面 --- 这部分代码
	// ---
	// 一分钟100次请求
	return ratelimit.NewBuilder(ratelimiter.NewRedisRateLimiter(redisClient, time.Minute, 100)).SetLogFunc(func(msg any, args ...any) {
		zl.Debug(fmt.Sprintf("%v", msg), logger.Field{Key: "error", Val: args})
	}).Build()
	// ---
}

// GetLoginJWTMiddleware 返回登录校验中间件
func GetLoginJWTMiddleware(ujwtHdl ijwt.UserJWTHandler, zl logger.Logger) gin.HandlerFunc {
	// 登录校验
	//login := middleware.NewLoginMiddlewareBuilder()
	login := middleware.NewLoginJWTMiddlewareBuilder(ujwtHdl, zl)
	return login.CheckLogin()
}

func GetLoggerMiddleware(zl logger.Logger) gin.HandlerFunc {
	ginLogger := accesslog.NewBuilder(func(ctx context.Context, al *accesslog.AccessLog) {
		zl.Debug("HTTP AccessLog",
			logger.Field{Key: "Method", Val: al.Method},
			logger.Field{Key: "URL", Val: al.URL},
			logger.Field{Key: "StatusCode", Val: al.StatusCode},
			logger.Field{Key: "Duration", Val: al.Duration},
			logger.Field{Key: "ReqBody", Val: al.ReqBody},
			logger.Field{Key: "RespBody", Val: al.RespBody},
		)
	})
	viper.WatchConfig()
	viper.OnConfigChange(func(in fsnotify.Event) {
		// 当配置文件变更时，更新日志记录的行为
		allowReqBody := viper.GetBool("config.log.allowReqBody")
		allowRespBody := viper.GetBool("config.log.allowRespBody")
		zl.Debug("Configuration updated",
			logger.Field{Key: "config.log.allowReqBody", Val: allowReqBody},
			logger.Field{Key: "config.log.allowRespBody", Val: allowRespBody},
		)
		ginLogger.AllowReqBody(allowReqBody).AllowRespBody(allowRespBody)
	})
	return ginLogger.AllowReqBody(viper.GetBool("config.log.allowReqBody")).AllowRespBody(viper.GetBool("config.log.allowRespBody")).Build()
}
